home *** CD-ROM | disk | FTP | other *** search
- ***********************************************************************
- This article is being presented through the *StarBoard* Journal of the
- FlagShip/StarShip SIGs (Special Interest Groups) on Delphi and GEnie
- telecommunication networks. Permission is hereby granted to non-profit
- organizations only to reprint this article or pass it along electronic-
- ally as long as proper credit is given to both the author and the
- *StarBoard* Journal.
- ***********************************************************************
-
- THE ABC's OF BCD (part 3)
-
- Manipulating Decimal Numbers In Machine Language
-
- by W.J. Brier
- (TROUBLESOME on DELPHI)
- ===========================================================================
- INTRODUCTION
-
- If you have thoroughly read THE ABC'S OF BCD parts 1 and 2, and have tried
- out the program examples that were presented, you should have an
- understanding of binary coded decimal (BCD) numbers and how to convert from
- ASCII to BCD and vice versa. You should also have a fairly clear
- understanding of how to format your ASCII numbers to produce a pleasing and
- professional-appearing display. If some of the concepts presented in part
- 2 are not clear, I suggest that you read over the material again and try
- out the program examples before continuing on with part 3 of this series.
-
- In part 1 you were presented with the basic concept of binary coded decimal
- numbers and were shown how to store such numbers. Routines were presented
- that illustrated the techniques of encoding a two digit ASCII number into a
- single BCD digit and decoding a single BCD digit back to ASCII numerals.
- Part 2 expanded on this knowledge by describing the techniques for encoding
- and decoding strings of digits, and formatting ASCII strings to suit the
- display needs of your program.
-
- In THE ABC'S OF BCD part 3, methods of transferring BCD numbers from one
- place to another in memory will be discussed. Programming techniques for
- performing addition and subtraction of BCD numbers will be described.
- Later, a complete evaluation routine will be presented that evaluates
- signed BCD numbers and performs the appropriate mathematical operations
- upon them. Program examples will continue to use the same symbology used
- in parts 1 and 2 of the series.
-
- I. TRANSFERRING BCD NUMBERS BETWEEN MEMORY LOCATIONS
-
- In all of the examples that have been presented in this series of articles,
- it was assumed that the BCD number to be operated upon was already in the
- BCD accumulator. Naturally, this will not always be the case. BCD numbers
- will be stored in various locations and will have to be transferred to one
- or both of the BCD accumulators to perform various operations. The result
- left in BCD accumulator #1 (ACUM1) will then have to be sent to some other
- location in memory. To accomplish these transfer operations some type of
- routine is needed to do the job.
-
- If you may recall, it was recommended that all BCD numbers be composed of
- the same number of digits in your program. Consistent length promotes
- easier conversion from ASCII to BCD and back and also makes it easy to
- transfer BCD numbers from one location to another.
-
- Also, if all BCD numbers are the same length, a terminator is not required
- at the end of the BCD number. With this in mind, let's see what it takes
- to move a BCD number from one location to another.
-
- Transferring a BCD number simply requires that you set up some pointers to
- the address of the BCD number to be transferred (the SOURCE location) and
- some pointers to the address of the location to which it is to be
- transferred (the DESTINATION location). Then, using a loop, the bytes are
- read one at a time from the source and stored at the destination location.
- In the example that follows, it is assumed that the BCD number is five
- bytes in length and that the zero page pointer locations are PTR1 and PTR2
- (each representing two bytes in zero page). If the concept of zero page
- and post-indexed addressing is a bit fuzzy to you, a good text on 6502
- machine language programming will be of value. Here is an algorithm that
- transfers a BCD number from the address contained in PTR1 and PTR1+1 to the
- address contained in PTR2 and PTR2+1:
-
- TRANS LDY #$04 ;OFFSET & COUNTER
- ;
- TRANS1 LDA (PTR1),Y ;FETCH BYTE FROM SOURCE
- STA (PTR2),Y ;STORE AT DESTINATION
- ;
- DEY ;LOWER OFFSET/COUNTER
- BPL TRANS1 ;IF NOT NEGATIVE THEN LOOP
- ;
- RTS ;OR ELSE EXIT
-
- This routine simply loops backwards, moving one byte at a time and ending
- when the .Y register WRAPS AROUND to $FF. Notice that the .Y register is
- set to $04 (not $05) at the beginning of the routine. Other length BCD
- numbers could be transferred with this routine by loading .Y with an appro-
- priate value.
-
- To use this subroutine, it is necessary to store the source and destination
- addresses in zero page pointers PTR1 and PTR2 respectively. Here is an
- example of how to transfer a number from location SRC to BCD accumulator
- ACUM1:
-
- LDX #<SRC ;SOURCE ADDRESS LOW BYTE
- LDY #>SRC ;SOURCE ADDRESS HIGH BYTE
- STX PTR1
- STY PTR1+1
- ;
- LDX #<ACUM1 ;ACUM1 ADDRESS LOW BYTE
- LDY #>ACUM1 ;ACUM1 ADDRESS HIGH BYTE
- STX PTR2
- STY PTR2+1
- ;
- JSR TRANS ;CALL TRANSFER ROUTINE
-
- program continues...
-
- Upon exiting from this program fragment ACUM1 will contain the same number
- as the source location SRC. SRC is undisturbed and the .Y register will
- contain $FF. The .X register is undisturbed as it is not used.
-
- The need to transfer BCD numbers between memory and the BCD accumulators
- will take on considerable importance when it becomes necessary to perform
- mathematical operations such as addition and subtraction. All mathematical
- operations will be initiated by storing one BCD number in ACUM1 and the
- other in ACUM2.
-
- II. ELEMENTARY MATHEMATICS IN MACHINE LANGUAGE
-
- Before this discussion takes up the subject of adding and subtracting BCD
- numbers, a brief review of machine language mathematics will be presented.
- This should facilitate easier understanding of multi-byte BCD evaluation.
-
- The 65XX/85XX microprocessor has instructions for performing single byte
- addition or subtraction. These instructions, as you may know, are ADC (add
- with carry) and SBC (subtract with borrow). There are also two
- instructions which determine whether these operations are to be performed
- in binary or decimal mode. They are SED (set decimal mode) and CLD (clear
- decimal mode). Almost all programs operate in binary mode and thus usually
- perform a CLD (which is also performed during the power-up process when you
- first turn on the computer).
-
- The effect of the CLD instruction is to cause a carry in addition when the
- result in the accumulator (.A register) exceeds $FF, whereas an SED
- instruction will cause a carry to occur if the value in .A exceeds $99. In
- both modes, a borrow during subtraction occurs if the value in .A is
- reduced to less than zero. However, several important effects occur in
- decimal mode which will now be discussed.
-
- To more clearly understand exactly what the CPU does when adding in decimal
- mode here is an example of adding two bytes of small value, one example in
- binary mode and one in decimal mode:
-
- BINARY DECIMAL
- ===============================
- $06 $06
- + $07 + $07
- ===============================
- $0D $13
-
- Notice that the sum on the left is the binary result of adding the two
- numbers. Six plus seven does indeed equal thirteen. The sum on the right
- reflects the effect of operating the CPU in decimal mode. An internal
- half-carry has been generated because the units column, when added, exceeds
- decimal nine. The CPU has performed BCD addition, and did not require any
- extra programming to take care of the carry from the units to the tens.
-
- Here is another example of the difference between binary and decimal mode
- addition:
-
- BINARY DECIMAL
- ===============================
- $16 $16
- + $15 + $15
- ===============================
- $2B $31
-
- The hex value $16 is equal to 22 in decimal and of course $15 is 21 in
- decimal. The two result in the sum of 43, which is $2B in hex. Addition
- has been performed in binary. Note, however, that the decimal example
- shows the effect of the half-carry from the units column to the tens
- column. Again, BCD addition has been performed.
-
- As with addition, running the CPU in decimal mode results in different
- behavior in subtraction than in binary mode. Again, here is an example of
- the difference:
-
- BINARY DECIMAL
- ===============================
- $16 $16
- - $07 - $07
- ===============================
- $0F $09
-
- If you figure out the result of the binary operation on the left you will
- see that $16 (22) minus $07 (7) does equal $0F (15). The decimal version
- demonstrates another feature of decimal mode. The CPU automatically
- generates an internal half-borrow to account for the subtraction of the
- units column. The result is BCD subtraction.
-
- None of the above examples take the carry bit in the CPU status register
- into account. But, ADC really means (AD)d with (C)arry and SBC signifies
- (S)u(B)tract with (C)arry. Therefore, the state of the carry bit must be
- known prior to commencing with either of these mathematical operations. If
- a multi-precision number (more than one byte) is to be added to another for
- example, the carry will be used to handle any overflow that may occur while
- addition is in progress. Similarly, in subtracting multi-precision
- numbers, the carry bit will act as an INVERTED BORROW, and will account for
- underflow while subtraction is in progress. Therefore, the carry bit must
- be cleared (CLC) prior to any addition and or set (SEC) prior to any
- subtraction.
-
- In adding or subtracting multi-precision numbers, the carry behaves
- differently in decimal mode than in binary mode. This is best illustrated
- with an example of adding two multi-precision numbers together in the two
- modes. It is assumed that the carry bit was cleared with a CLC instruction
- prior to the actual addition operation:
-
- BINARY DECIMAL
- ===============================
- $04 $86 $04 $86
- + $03 $77 + $03 $77
- ===============================
- $07 $FD $08 $63
-
- In the binary example addition of the right hand column did not generate a
- carry as the sum of $86 and $77 did not exceed the $FF limit of the
- accumulator. Thus the sum of the left hand column was unaffected by the
- addition of the right hand column. In the decimal operation, note that a
- carry did occur to the left hand column, and that a half-carry occurred in
- the right hand column. The carry to the left resulted because the value in
- .A exceeded $99. In decimal mode such an event sets the carry bit, causing
- it to be added into the next operation. Therefore, the left hand column is
- really $04 + $03 + $01 (the carry bit) resulting in $08. The result is
- multi-precision BCD addition.
-
- A similar effect can be seen in multi-precision BCD subtraction. Again, an
- example will be presented to illustrate the difference between binary and
- decimal mode. It is assumed that the carry bit has been set with an SEC
- instruction prior to subtraction:
-
- BINARY DECIMAL
- ===============================
- $04 $77 $04 $77
- - $03 $88 - $03 $88
- ===============================
- $00 $EF $00 $89
-
- In the binary mode operation, subtraction of the right hand column resulted
- in a borrow from the left column, as was also the case in the decimal mode
- operation. Note however, that a half-borrow had to be performed in the
- decimal mode operation, as subtracting the units in the right hand column
- would have resulted in a negative value. No such half-borrow is ever
- performed in binary mode.
-
- To summarize the effect upon the carry bit in addition and subtraction, the
- carry is set in binary mode if the result of addition exceeds $FF or 255.
- In decimal mode the carry is set if addition causes the result to exceed
- $99. Upon exceeding $99, the .A register wraps around to $00, whereas this
- wraparound in binary mode does not occur until the result exceeds $FF.
-
- Subtraction clears the carry bit if the result is less than zero in either
- case. However, going below zero in binary mode wraps the accumulator
- around to $FF while in decimal mode it wraps around to $99. Also, in
- decimal mode the CPU automatically takes care of the half-carry or
- half-borrow between the units and tens columns when adding or subtracting.
- This half-carry or half-borrow never occurs in binary mode.
-
- Understanding the essentials of addition and subtraction naturally leads
- into addition of BCD numbers, the subject of the next chapter.
-
- III. ADDING MULTI-PRECISION BCD NUMBERS
-
- Addition of multi-precision BCD numbers is accomplished in the same manner
- as in adding binary numbers, the difference being the manner in which the
- carry is handled. Before going to multi-precision addition, let's review
- the procedure for adding two bytes.
-
- To add two bytes the .A register is loaded with one of the bytes and an add
- with carry (ADC) operation is performed using the other byte as an operand.
- The result is deposited in the .A register. To avoid having the carry bit
- included in the sum a CLC instruction must be issued prior to actually
- performing addition. So, to add NUM1 to NUM2 you would code the following:
-
- BINARY DECIMAL
- ================================
- SED
- CLC CLC
- LDA NUM1 LDA NUM1
- ADC NUM2 ADC NUM2
- CLD
- ================================
-
- Exiting from this routine results in the sum of NUM1 and NUM2 being left in
- the .A register. The most obvious difference between the two techniques is
- the use of the SED (set decimal mode) and CLD (clear decimal mode)
- instructions in the decimal version. Otherwise, the two examples operate
- in exactly the same fashion. As described above, the SED instruction will
- cause an internal half-carry to be generated if the units column exceeds
- nine when added.
-
- To perform multi-precision BCD addition the routine illustrated above must
- be incorporated into a loop. The loop starts with the least significant
- digits and successively adds columns to produce the sum for the two
- numbers. The carry is cleared only prior to entering the loop. Then as
- the loop progresses, any carry that is generated is added in the next
- column, resulting in correct addition. This technique functions on the
- assumption that both BCD numbers are the same length (five bytes in this
- case).
-
- Assuming that one BCD number is stored in ACUM1 and the other in ACUM2,
- here is a routine to add the two numbers together and store the sum in
- ACUM1:
-
- ADD SED ;SET DECIMAL MODE
- CLC ;CLEAR CARRY BIT
- LDY #$04 ;OFFSET & COUNTER
- ;
- ADD1 LDA ACUM1,Y ;FETCH BCD DIGIT
- ADC ACUM2,Y ;ADD
- STA ACUM1,Y ;SAVE RESULT
- ;
- DEY ;NEXT DIGIT
- BPL ADD1 ;NOT DONE, SO LOOP
- ;
- CLD ;RETURN TO BINARY MODE
- ;
- RTS ;AND EXIT
-
- Upon exiting from this routine, ACUM1 will contain the sum of the two BCD
- numbers, ACUM2 will be undisturbed, the .A register will contain the most
- significant BCD digit that resulted from the addition, the .Y register will
- contain $FF and the .X register will be undisturbed. A total of five
- iterations will be performed.
-
- To better understand just how this routine actually adds two BCD numbers,
- here is is an example of what ACUM1 would look like at each loop through
- the routine (values are hex numbers and ACUM2 is not shown for each loop as
- it doesn't change):
-
- LOOP ACUM1 ACUM2 CARRY
- ===============================================
- START 18 57 21 00 91 46 57 00 82 10 0
-
- 1 18 57 21 00 01 1
- 2 18 57 21 83 01 0
- 3 18 57 21 83 01 0
- 4 18 14 21 83 01 1
- 5 65 14 21 83 01 0
- ===============================================
-
- Try coding the above routine and entering these numbers to see for yourself
- that:
-
- $18 $57 $21 $00 $91
- + $46 $57 $00 $82 $10
- =====================
- $65 $14 $21 $83 $01
-
- is indeed the result of BCD addition. To see the effect of not having the
- CPU in decimal mode, replace the SED instruction with a NOP instruction and
- run the routine again with the same numbers. Needless to say, the result
- will be very different.
-
- While the subroutine presented above does correctly add two BCD numbers it
- does not check for possible overflow of either the most significant digit
- or the BCD accumulator itself. Consider the following operation:
-
- 41 00 00 00 00
- + 40 00 00 00 00
- ================
- 81 00 00 00 00
-
- What's wrong with it, you say? The result is actually a negative number
- because the sign bit has been set. The most significant digit has been
- overflowed. When the number is decoded into ASCII the result will be
- -100000000, not 8100000000 as you might expect. Clearly, some means of
- avoiding an overflow into the sign bit must be devised.
-
- It is also possible to overflow the entire BCD accumulator. If the
- following operation is performed:
-
- 50 00 00 00 00
- + 60 00 00 00 00
-
- the BCD accumulator will contain:
-
- 10 00 00 00 00
-
- and the carry (C) flag will be set. The correct value should be:
-
- 01 10 00 00 00 00
-
- However, there isn't a sixth byte in the BCD accumulator. The result is an
- erroneous value. Therefore, in addition to avoiding an overflow into the
- sign bit an overflow of the accumulator must also be prevented.
-
- Detecting an accumulator overflow is quite easy as the C flag will be set
- if such an overflow did occur. Indentifying an overflow into the sign bit,
- at first glance, doesn't seem to be all that easy to accomplish, as the C
- flag will be cleared upon leaving the ADD subroutine. Fortunately, the
- overflow (V) flag in the status register is tailor-made for this purpose.
- The V flag is set to one any time a mathematical or logical operation on
- the .A register results in a binary value in excess of 0111 1111 or $7F.
-
- For example, the following binary operation:
-
- 0111 1111 ($7F)
- + 0000 0001 ($01)
- =================
- 1000 0000 ($80)
-
- will cause the V flag to be set. This is because the addition of the two
- numbers generated a binary carry from the sixth to the seventh bit. Thus,
- the sixth bit has been overflowed into the seventh bit.
-
- When calling the ADD routine the last addition operation performed is upon
- the most significant digit, which happens to contain the sign bit. If the
- result of that operation overflows the sign bit, the V flag will be set
- upon exit from the routine. If no overflow occurred, the V flag will be
- cleared. Therefore, testing the V flag is all that is required to detect
- the sign bit overflow. Coupled with a C flag test for accumulator
- overflow, execution of the program can be re-directed to an error routine
- that advises the user of the overflow. Coding would take this form:
-
- JSR ADD ;ACUM1 = ACUM1 + ACUM2
- BCC NEXT ;NO ACCUMULATOR OVERFLOW
- ;
- OVRFLO JMP ERROR ;INFORM USER OF OVERFLOW
- ;
- NEXT BVS OVRFLO ;SIGN BIT OVERFLOW
-
- program continues...
-
- The ERROR routine would display a suitable error message to the user and
- would halt further number processing. Assuming that neither type of error
- occurred then the program would continue to the next step.
-
- To briefly review, addition of BCD numbers is performed by placing one
- number into BCD accumulator ACUM1 and the other into ACUM2. A subroutine
- is called which iteratively adds the digits of the two numbers. Upon exit
- from the addition subroutine, the C and V flags are tested for a possible
- overflow.
-
- IV. SUBTRACTING MULTI-PRECISION BCD NUMBERS
-
- As with multi-precision addition, subtraction of multiprecision BCD numbers
- is accomplished in the same manner as in subtracting binary numbers, again
- the difference being the manner in which the carry is handled. Before
- going to multi-precision subtraction, let's review the procedure for
- subtracting two bytes.
-
- To subtract two bytes the .A register is loaded with one of the bytes (the
- minuend) and a subtract with carry (SBC) operation is performed using the
- other byte (the subtrahend) as an operand. The difference is deposited in
- the .A register. To avoid having the carry bit influence the difference a
- set carry (SEC) instruction must be issued prior to actually performing
- subtraction. In subtraction the carry functions as an inverted borrow.
-
- So, to subtract NUM1 to NUM2 you would code the following:
-
- BINARY DECIMAL
- ================================
- SED
- SEC SEC
- LDA NUM1 LDA NUM1
- SBC NUM2 SBC NUM2
- CLD
- ================================
-
- Exiting from this routine results in the difference of NUM1 and NUM2 being
- left in the .A register. Again, the difference between the two techniques
- is the use of the SED and CLD instructions in the decimal version.
- Otherwise, the two examples operate in exactly the same fashion. The SED
- instruction will cause an internal half-borrow to be generated if the units
- column goes below zero when subtracted.
-
- To perform multi-precision BCD subtraction the routine illustrated above
- must be incorporated into a loop. The loop starts with the least
- significant digits and successively subtracts columns to produce the
- difference for the two numbers. The carry is set only prior to entering
- the loop. As with the addition subroutine, this technique functions on the
- assumption that both BCD numbers are the same length (five bytes in this
- case).
-
- Assuming that the minuend has been stored in ACUM1 and the subtrahend in
- ACUM2, here is a routine to subtract the two numbers and store the
- difference in ACUM1:
-
- SUB SED ;SET DECIMAL MODE
- SEC ;SET CARRY BIT
- LDY #$04 ;OFFSET & COUNTER
- ;
- SUB1 LDA ACUM1,Y ;FETCH MINUEND
- SBC ACUM2,Y ;SUBTRACT SUBTRAHEND
- STA ACUM1,Y ;SAVE DIFFERENCE
- ;
- DEY ;NEXT DIGIT
- BPL SUB1 ;NOT DONE, SO LOOP
- ;
- CLD ;RETURN TO BINARY MODE
- ;
- RTS ;AND EXIT
-
- Upon exiting from this routine, ACUM1 will contain the difference of the
- two BCD numbers, ACUM2 will be undisturbed, the .A register will contain
- the most significant BCD digit that resulted from the subtraction, the .Y
- register will contain $FF and the .X register will be undisturbed. A total
- of five iterations will be performed.
-
- To better understand just how this routine actually subtracts two BCD
- numbers, here is is an example of what ACUM1 would look like at each loop
- through the routine (values are hex numbers and ACUM2 is not shown for each
- loop as it doesn't change):
-
- LOOP ACUM1 ACUM2 CARRY
- ===============================================
- START 65 14 21 83 01 46 57 00 82 10 1
-
- 1 65 14 21 83 91 0
- 2 65 14 21 00 91 1
- 3 65 14 21 00 91 1
- 4 65 57 21 00 91 0
- 5 18 57 21 00 91 1
- ===============================================
-
- Try coding the above routine and entering these numbers to see for yourself
- that:
-
- $65 $14 $21 $83 $01
- - $46 $57 $00 $82 $10
- =====================
- $18 $57 $21 $00 $91
-
- is indeed the result of BCD subtraction. To see the effect of not having
- the CPU in decimal mode, replace the SED instruction with a NOP instruction
- and run the routine again with the same numbers. Needless to say, the
- result will be very different.
-
- Although the above routine does subtract two BCD numbers, it doesn't
- account for the possibility of a negative difference, which would be the
- result of the subtrahend being greater than the minuend. Attempting to
- subtract:
-
- $49 $99 $99 $99 $99
- - $50 $00 $00 $00 $00
-
- should result in:
-
- - $00 $00 $00 $00 $01
-
- which in the system of signed numbers being used would be:
-
- $80 $00 $00 $00 $01
-
- the set seventh bit of the most significant digit indicating that this is a
- negative number. However, the actual result left in ACUM1 will be:
-
- $99 $99 $99 $99 $99
-
- This result is called the NINES COMPLEMENT of the difference. The nines
- complement occurs due to the fact that when in decimal mode the .A register
- wraps around to $99 when the value it contains goes below zero. Here is a
- table of nines complements and the actual values that they represent (all
- values are in hex):
-
- NINES ACTUAL
- ============================
- 99 01
- 98 02
- 97 03
- ---- ----
- 50 50
- ---- ----
- 03 97
- 02 98
- 01 99
- 00 00
- ============================
-
- As can be seen from examining the table there is a definite relationship
- between the actual value and its nines complement. One can deduce from
- this relationship that if the nines complement value is subtracted from
- zero (with the CPU in decimal mode) the result will be the actual value.
- This could be considered taking the nines complement of the nines
- complement. The resulting inversion produces a proper decimal value.
-
- The subtraction routine given above must be modified to properly handle
- nines complement results by performing the additional subtraction required
- to change the nines complement back to a true decimal value. However, it
- is necessary to determine when the result is actually a nines complement
- and when it isn't. Fortunately, the carry (C) flag will indicate if the
- last subtraction resulted in a negative value. If upon leaving the
- subtraction loop, the C flag is set, the result is positive and no further
- action is required. If the C flag is cleared, then a borrow occurred on
- the last subtraction and a nines complement number has been left in ACUM1.
- The inversion of the ACUM1 value must be performed.
-
- Here is the modified routine:
-
- SUB SED ;DECIMAL MODE
- SEC ;SET CARRY FOR SUBTRACTION
- LDY #$04 ;OFFSET & COUNTER
- ;
- SUB1 LDA ACUM1,Y ;FETCH MINUEND
- SBC ACUM2,Y ;SUBTRACT SUBTRAHEND
- STA ACUM1,Y ;STORE DIFFERENCE
- ;
- DEY ;LOWER OFFSET
- BPL SUB1 ;NEXT ITERATION
- ;
- BCS SUB3 ;RESULT WAS POSITIVE
- ;
- ;RESULT IS NINES COMPLEMENT
- ;
- SEC ;SET CARRY FOR SUBTRACTION
- LDY #$04 ;OFFSET & COUNTER
- ;
- SUB2 LDA #$00
- SBC ACUM1,Y ;INVERT NINES COMPLEMENT
- STA ACUM1,Y ;STORE DIFFERENCE
- ;
- DEY ;LOWER OFFSET
- BPL SUB2 ;NEXT ITERATION
- ;
- ORA #$80 ;SET SIGN BIT...
- STA ACUM1 ;TO SHOW NEGATIVE NUMBER
- ;
- SUB3 CLD ;BINARY MODE
- ;
- RTS ;EXIT
-
- The first portion of the routine is the same subtraction algorithm that was
- presented earlier. However, upon completion of the last subtraction
- operation, the carry (C) flag is tested for a possible negative result. If
- the carry is set, the routine branches to the end where the decimal flag is
- cleared. If the C flag is cleared then a borrow occurred on the last
- subtraction operation and the result is a nines complement number.
-
- To invert the nines complement number, subtraction is performed once again,
- with the nines complement value now the subtrahend and zero as the minuend.
- The effect is to invert the nines complement value resulting in the actual
- difference. Lastly, the sign bit is set to indicate that the difference is
- negative.
-
- Looking back in review, subtraction of multi-precision BCD numbers is
- performed by placing the minuend into BCD accumulator #1 (ACUM1) and
- placing the subtrahend into ACUM2. The carry and decimal flags are set and
- ACUM2 is iteratively subtracted from ACUM1. At the end of the subtraction
- the C flag is tested for a borrow.
-
- If the flag is set, the difference is positive. If the flag is cleared the
- difference is a nines complement value and must be inverted. Following
- inversion, the sign bit is set in ACUM1 to indicate that the result is a
- negative number.
-
- V. SIGNED BCD NUMBER EVALUATION
-
- The mathematical routines that have been presented up to this point perform
- either addition or subtraction. They operate only on unsigned numbers
- (although the subtraction routine will generate a signed result if
- necessary). Obviously, some type of evaluation routine is needed that can
- determine from the signs of the numbers what operations have to be invoked.
- This routine would have to determine the signs of the BCD accumulators and
- then, based on the signs, select the correct operation (addition or
- subtraction). Following the actual math operation, the evaluation would
- have to conclude by restoring the correct sign to the result. A special
- case would be required when subtraction results in a difference of zero.
-
- The initial action in the evaluation routine would be to clear the sign
- flags SFLG1 and SFLG2 to zero. This would indicate that the BCD
- accumulators both contain positive numbers (in this evaluation zero is
- considered positive). Next, the first byte of ACUM1 would be tested for
- negativity and if found to be negative, SFLG1 would be set to indicate this
- condition. Because the addition and subtraction routines operate only on
- unsigned numbers, it would also be necessary to mask the sign bit of ACUM1.
- The same evaluation would then be performed on ACUM2 with the sign bit
- being masked and SFLG2 set if ACUM2 is negative. Here is the sign checking
- portion of the evaluation routine:
-
- EVAL LDA #$00
- STA SFLG1 ;CLEAR SIGN FLAGS
- STA SFLG2
- ;
- LDA ACUM1 ;CHECK SIGN
- BPL EVAL1 ;POSITIVE NUMBER
- ;
- AND #%01111111 ;MASK SIGN BIT w/$7F
- STA ACUM1
- ;
- DEC SFLG1 ;SET SIGN FLAG TO NEGATIVE
- ;
- EVAL1 LDA ACUM2 ;CHECK SIGN
- BPL EVAL2 ;POSITIVE NUMBER
- ;
- AND #%01111111 ;MASK SIGN BIT
- STA ACUM2
- ;
- DEC SFLG2 ;SET SIGN FLAG
- ;
- EVAL2 program continues...
-
- Upon reaching EVAL2 in this program fragment each sign flag will be set or
- cleared according to the sign of the number in the associated BCD
- accumulator, and the sign bit will be cleared if the number is negative.
-
- The function of ANDing the first byte of each accumulator is to dispose of
- the sign bit. For example, this is how the operation would affect the
- first byte of the BCD number in ACUM1 if it was negative:
-
- ACUM1 %1000 0111 = $87
- AND %0111 1111 = $7F
- ======================
- ACUM1 %0000 0111 = $07
-
- As can be seen, the sign bit has been cleared and the byte contains only a
- valid BCD digit. This is stored back into the BCD accumulator.
-
- With the sign evaluation completed, the next step would be to determine
- which math operation (addition or subtraction) must be performed. Some
- generalizations can be made at this point. If the signs of the two numbers
- are the same (both positive or both negative) the numbers must be added.
- If the signs are opposite, they must be subtracted. If subtraction is
- performed, the sign of the difference must be inverted if the difference is
- not zero and if the minuend is negative. Perhaps the best way to visual-
- ize these relationships is to construct a TRUTH TABLE of the various
- permutations that can result. In the table, ACUM1 will be referred to as
- A1 and ACUM2 will be A2:
-
- A1 A2 OPER. ==> A1
- ========================
- + + ADD +
- + - SUB *
- - - ADD -
- - + SUB *
- =======================
-
- The asterisks following the SUB operation indicate that the sign depends on
- whether the absolute (unsigned) value of the minuend was larger or smaller
- than that of the subtrahend. To make a determination of such a case
- another truth table is needed:
-
- EXPRESSION SFLG1 RESULT
- ============================
- A1 > A2 + +
- A1 > A2 - -
- A1 < A2 + -
- A1 < A2 - +
- A1 = A2 * 0
- ============================
-
- It is apparent from studying this table that if the sign of ACUM1 is
- positive, the sign of the difference from subtraction will be correct. If
- ACUM1 is negative then the sign of the difference will be the opposite of
- what it should be and therefore must be inverted by logical operations. If
- the difference is zero the sign will always be positive. Thus a check for
- a zero difference must be made after subtraction has been completed. This
- could be structured into a subroutine for use by any other part of your
- program that needs to detect whether the BCD number in ACUM1 is zero. Here
- is a zero checking subroutine:
-
- ZCHK CLC ;CARRY IS ZERO FLAG
- LDY #$04 ;OFFSET
- ;
- ZCHK1 LDA ACUM1,Y ;FETCH DIGIT
- BNE ZCHKO ;BCD NUMBER NOT ZERO
- ;
- DEY
- BPL ZCHK1 ;LOOP
- ;
- SEC ;INDICATE ZERO
- ;
- ZCHKO RTS ;EXIT
-
- Upon exit from this routine the carry flag will be set if the BCD number in
- ACUM1 equals zero. As usual, the initial value for the .Y register assumes
- a BCD number length of five digits.
-
- One other operation that the evaluation subroutine must also perform is
- trapping an overflow of ACUM1 or of the sign bit. Here is the complete
- evaluation subroutine:
-
- EVAL LDX #$00
- STX SFLG1 ;CLEAR SIGN FLAGS
- STX SFLG2
- ;
- LDA ACUM1 ;CHECK SIGN
- BPL EVAL1 ;POSITIVE NUMBER
- ;
- AND #$7F ;MASK SIGN BIT
- STA ACUM1
- ;
- DEC SFLG1 ;SET SIGN FLAG TO NEGATIVE
- ;
- EVAL1 LDA ACUM2 ;CHECK SIGN
- BPL EVAL2 ;POSITIVE NUMBER
- ;
- AND #$7F ;MASK SIGN BIT
- STA ACUM2
- ;
- DEC SFLG2 ;SET SIGN FLAG
- ;
- EVAL2 LDA SFLG1
- CMP SFLG2 ;COMPARE SIGNS
- BNE EVAL4 ;OPPOSITE, SO SUBTRACT
- ;
- JSR ADD ;ADD
- BCS EVAL3 ;OVERFLOW
- ;
- BVC EVAL5 ;NO SIGN BIT OVERFLOW
- ;
- SEC ;SIGN BIT OVERFLOW
- ;
- EVAL3 RTS ;ABORT DUE TO OVERFLOW
- ;
- EVAL4 JSR SUB ;SUBTRACT
- ;
- BIT ACUM1 ;CHECK DIFFERENCE SIGN
- BMI EVAL5 ;NEGATIVE DIFFERENCE
- ;
- JSR ZCHKO ;CHECK FOR ZERO RESULT
- BCS EVALO ;ZERO, NO SIGN CHANGE
- ;
- EVAL5 BIT SFLG1 ;CHECK SIGN
- BPL EVALO ;NO SIGN CHANGE
- ;
- LDA ACUM1 ;
- EOR #%10000000 ;INVERT SIGN BIT
- STA ACUM1 ;SAVE
- ;
- EVALO CLC ;NO OVERFLOW ERROR
- ;
- RTS ;EXIT
-
- As already discussed, the first portion of the routine checks the signs of
- the two BCD numbers and performs appropriate operations if one or both
- signs are negative. At EVAL2 the sign flags are checked for equality.
-
- As pointed out before, if the signs are the same, the numbers must be
- added. This is accomplished by calling the ADD subroutine (described in
- chapter III). Otherwise, the numbers are subtracted by calling the SUB
- subroutine (described in chapter IV).
-
- Following a call to ADD the evaluation will abort with the carry (C) flag
- set if an overflow occurred during addition (the set C flag indicates an
- overflow error to the portion of the program that called EVAL). If no
- overflow occurred the routine will branch to EVAL5 where the original sign
- of ACUM1 will be tested. If that sign was negative the sign bit of ACUM1
- will be set to indicate negativity. This is accomplished by TOGGLING bit
- seven of the most significant byte of ACUM1 (using the EOR instruction).
- The effect of this is to set a cleared bit or clear a set bit. From the
- standpoint of addition, the toggling effect isn't important because addi-
- tion always results in an unsigned number. Thus, the only effect of the
- EOR (exclusive-or) instruction is to set the sign bit.
-
- Upon leaving the SUB subtraction subroutine, EVAL calls ZCHK to check for a
- zero result (unless the sign of ACUM1 is negative). If the result was zero
- the sign check is skipped and the routine ends. Otherwise execution
- continues through to the sign check at EVAL5. Here is where the toggling
- effect of the exclusive-or function becomes important. According to the
- second truth table (above) if the sign of ACUM1 is positive then the sign
- of the difference is correct. Otherwise, the sign must be inverted. EOR
- will always toggle the sign bit to the opposite state, thus automatically
- performing the inversion.
-
- The last operation performed before exiting EVAL is to clear the carry flag
- (CLC), thus indicating that the evaluation was completed without an
- overflow error.
-
- To use EVAL, the two BCD numbers to be evaluated must be deposited into the
- BCD accumulators. Then, the next operation would be to actually call the
- EVAL subroutine and to check for a possible overflow error:
-
- JSR EVAL ;EVALUATE BCD NUMBERS
- BCC TOHERE ;NO OVERFLOW ERROR
- ;
- JMP ERROR ;INDICATE ERROR TO USER
- ;
- TOHERE program continues...
-
- Upon reaching TOHERE in the above program fragment your next step should be
- to transfer the result in ACUM1 back to a suitable location in memory.
-
- VI. RECAP
-
- In THE ABC'S OF BCD part 3 you have learned how to transfer BCD numbers
- from one location in memory to another, and how to add and subtract
- multi-precision numbers. You have been guide through the technique of
- evaluating signed BCD numbers and have been presented with a complete
- evaluation subroutine that utilizes the addition and subtraction techniques
- that have been described in chapters III and IV of this article. You now
- have the ability to perform BCD math within your machine language program.
- In THE ABC'S OF BCD part 4 the techniques of multiplying will be presented,
- thus giving the tools you need to set up three of the four basic math
- functions.
-